﻿using System.Configuration;
using System.Windows.Forms;
using System.Data;
using System.Xml;
using SdmxMl.Helper;
using SdmxMl.Common;

namespace SdmxMl.Structure
{
    /// <summary>
    /// The category is given a human-readable name (which may be in multiple
    /// language-specific versions), and may optionally have a human-readable
    /// description (also in multiple, landuage-specific versions). Annotations
    /// may be provided in the Annotations element. References to dataflows and
    /// metadataflows may be provided. The Category element represents a set of
    /// nested categories which are child categories. The Category must have a
    /// unique ID within the Category Scheme provided in the id attribute.
    /// A version may also be supplied - if ommitted, the version is understood
    /// to be "1.0". The urn attribute holds a valid registry URN (see the SDMX
    /// Registry Specification).
    /// </summary>
    public class Category : BaseArtefact, ISdmxHierarchyListItem
    {
        #region Properties

        public Category ParentCategory { get; set; }

        public NestedHierarchicalList RootHierarchy { get; set; }

        public virtual string Urn { get; set; }

        public virtual string Uri { get; set; }

        public bool IsExternalReference { get; set; }

        public DataFlowrefList DataFlowRefList { get; set; }

        public MetaDataFlowrefList MetaDataFlowRefList  { get; set; }

        public CategoryList CategoryList { get; set; }
        
        public IHierarchyManage InnerHierarchy
        {
            get { return CategoryList; }
        }

        public string FullId
        {
            get
            {
                Category c = this;
                string i = Id;
                while (c.ParentCategory != null)
                {
                    i = c.ParentCategory.Id + "." + i;
                    c = c.ParentCategory;
                }
                return i;
            }
        }
        /// <summary>Category root item</summary>
        public Category RootItem
        {
            get
            {
                Category c = this;
                while (c.ParentCategory != null)
                {
                    c = c.ParentCategory;
                }
                return c;
            }
        }

        // For ISdmxHierarchyListItem compatibility
        public string Parent
        {
            get
            {
                return ParentCategory != null ? ParentCategory.Id : string.Empty;
            }
            set { }
        }

        #endregion  //Properties

        #region Construction

        private void Init()
        {
            DataFlowRefList = new DataFlowrefList();
            MetaDataFlowRefList = new MetaDataFlowrefList();
            CategoryList = new CategoryList(RootHierarchy);
        }

        public Category(NestedHierarchicalList root, Category parentCat)
        {
            RootHierarchy = root;
            ParentCategory = parentCat;
            Init();

        }
        public Category(NestedHierarchicalList root, Category parentCat, XmlNode nodeArtefact, SdmxNamespaceManager nsmgr) 
            : base(nodeArtefact, nsmgr)
        {
            RootHierarchy = root;
            ParentCategory = parentCat;
            Init();
            Urn = Xmlhelper.GetAtt(nodeArtefact, "urn");
            Uri = Xmlhelper.GetAtt(nodeArtefact, "uri");

            IsExternalReference = Xmlhelper.GetAtt(nodeArtefact, "isExternalReference").ToLower() == "true";

            FillCategoryList(CategoryList, nodeArtefact, nsmgr);
        }

        private void FillCategoryList(CategoryList cl, XmlNode nodeArtefact, SdmxNamespaceManager nsmgr)
        {
            XmlNodeList nodeFlowList = nodeArtefact.SelectNodes("./structure:MetadataflowRef", nsmgr);
            foreach (XmlNode nd in nodeFlowList)
            {
                MetaDataFlowRef mf = new MetaDataFlowRef(nd, nsmgr);
                MetaDataFlowRefList.Add(mf);
            }

            nodeFlowList = nodeArtefact.SelectNodes("./structure:DataflowRef", nsmgr);
            foreach (XmlNode nd in nodeFlowList)
            {
                DataFlowRef df = new DataFlowRef(nd, nsmgr);
                DataFlowRefList.Add(df);
            }

            string query = "./structure:Category";
            XmlNodeList nodeList = nodeArtefact.SelectNodes(query, nsmgr);
            if (nodeList.Count > 0)
            {
                foreach (XmlNode nd in nodeList)
                {
                    Category c = new Category(RootHierarchy, this, nd, nsmgr);
          //          if (c.AnnotationList.GetAnnotationType("CW") != null)
          //          {
                        cl.Add(c);
          //              c.AnnotationList.RemoveAnnotationType("CW");
          //          }
                }

            }
        }

        #endregion // Construction

        #region Clone

        public ISdmxHierarchyListItem Clone()
        {
            Category c = new Category(null, null);
            c.Id = Id;
            c.Name = Name.Clone();
            c.Description = Description.Clone();
            c.Uri = Uri;
            c.Urn = Urn;

            c.AnnotationList = AnnotationList.Clone();

            return c;
        }
        #endregion

        #region XML serialize

        public void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);
            // id required
            if (string.IsNullOrEmpty(Id))
                Id = "undefined";
            xw.WriteAttributeString("id", Id);

            if (Version != "1.0")
                Xmlhelper.WriteAttributeIfExist(xw, "version", Version);
            Xmlhelper.WriteAttributeIfExist(xw, "uri", Uri);
            Xmlhelper.WriteAttributeIfExist(xw, "urn", Urn);
            if (IsExternalReference)
                xw.WriteAttributeString("isExternalReference", IsExternalReference.ToString().ToLower());

            Name.WriteXml(xw, prefix, "Name");
            Description.WriteXml(xw, prefix, "Description");

            // All Flow ref
            foreach (DataFlowRef df in DataFlowRefList)
                df.WriteXml(xw, prefix, "DataflowRef");
            foreach (MetaDataFlowRef mdf in MetaDataFlowRefList)
                mdf.WriteXml(xw, prefix, "MetadataflowRef");

            foreach (Category cat in CategoryList)
                cat.WriteXml(xw, prefix, "Category");


            // Annotations
            AnnotationList.WriteXml(xw, prefix, null);

            xw.WriteEndElement();
        }

        #endregion // XML serialize

        #region Tree

        public int UsedImageIndex
        {
            get
            {
                return TreeImgHelper.ImgIndexFromAnnotation(this.AnnotationList, 5);
            }
        }

        public override TreeNode FillNode(TreeNode parentNode)
        {
            TreeNode t = base.FillNode(parentNode);
            t.ImageIndex = t.SelectedImageIndex = UsedImageIndex;

            foreach (Category c in CategoryList)
                c.FillNode(t);
            return t;
        }
        #endregion //Tree

        #region String Representation

        public override string ToString()
        {
            return FullId + " - " + Name.ToString(); ;
            
        }
        #endregion // String Representation

    }

    #region Category List container

    /// <summary>
    /// Holds a list of category in a certain level of a category Scheme.
    /// </summary>
    public class CategoryList : NestedHierarchicalList
    {
        public CategoryList(NestedHierarchicalList root)
            : base(root)
        {
        }
        public override ISdmxHierarchyListItem GetParent(ISdmxHierarchyListItem it)
        {
            Category c = it as Category;
            if (c != null)
                return c.ParentCategory;
            return null;
        }

        public override ISdmxHierarchyListItem CreateNewItem(ISdmxHierarchyListItem parentItem)
        {
            Category parentCat = parentItem as Category;
            Category cat = new Category(this, parentCat);
            return cat;
        }

        public IHierarchyManage GetHierarchyManageOfItem(Category cat)
        {
            if (cat.ParentCategory == null)
                return rootHierarchy;
            return cat.ParentCategory.InnerHierarchy;
        }

        public override string ToString()
        {
            return "Categories";
        }

        protected override void FillTableRepresentation(NestedHierarchicalList cur, DataTable dataTable)
        {
            NestedHierarchicalList nhl = cur == null ? rootHierarchy : cur;
            foreach (Category it in nhl)
            {
                dataTable.Rows.Add(new object[] { it.FullId, it.Name });
                FillTableRepresentation((it.InnerHierarchy as NestedHierarchicalList), dataTable);
            }
        }

        /// <summary>
        /// Override to place full ID as information. Because ID can be same at differents levels.
        /// </summary>
        /// <param name="cur"></param>
        /// <param name="dataTable"></param>
        /// <param name="item"></param>
        protected override void FillPossibleParent(NestedHierarchicalList cur, DataTable dataTable, ISdmxHierarchyListItem item)
        {
            NestedHierarchicalList nhl = cur == null ? rootHierarchy : cur;
            foreach (Category it in nhl)
            {
                if (item != it)
                {
                    dataTable.Rows.Add(new object[] { it.FullId, it.Name, it });
                    FillPossibleParent((it.InnerHierarchy as NestedHierarchicalList), dataTable, item);
                }
            }
        }

        public Category LocateItemFromFullId(string fullId, NestedHierarchicalList nhl)
        {
            Category ret = null;

            foreach (Category cat in nhl)
            {
                if (cat.FullId == fullId)
                {
                    ret = cat;
                    break;
                }
                else
                {
                    Category rep = LocateItemFromFullId(fullId, cat.InnerHierarchy as NestedHierarchicalList);
                    if (rep != null)
                        ret = rep;
                }
            }
            return ret;
        }

        public override void RedefineParent(ISdmxHierarchyListItem item, string newParentId, object newOwner)
        {
            NestedHierarchicalList oldParent = LocateOwner(rootHierarchy, (item as Category), false);
            NestedHierarchicalList newParent = null;
            Category newParentCat = newOwner as Category;

            if (newOwner != null)
                newParent = LocateOwner(rootHierarchy, newParentCat, true);
            else
                newParent = rootHierarchy;

            if (oldParent != null && newParent != null && oldParent != newParent)
            {
                oldParent.Remove(item);
                newParent.Add(item);
                foreach (Category cat in newParent)
                    cat.ParentCategory = newParentCat;
            }
        }

        private NestedHierarchicalList LocateOwner(NestedHierarchicalList nhl, Category cat, bool getInnerHierarchy)
        {
            NestedHierarchicalList loc = null;
            NestedHierarchicalList cur = null;
            foreach (Category it in nhl)
            {
                if (it == cat)
                {
                    if (getInnerHierarchy == false)
                        loc = nhl;
                    else
                        loc = it.InnerHierarchy as NestedHierarchicalList;
                    break;
                }
                else
                {
                    cur = LocateOwner((it.InnerHierarchy as NestedHierarchicalList), cat, getInnerHierarchy);
                    if (cur != null)
                        loc = cur;
                }
            }
            return loc;
        }

    }
    #endregion
}
